Docker 使用Dockerfile创建镜像

1 本章背景知识

1、Dockerfile 是一个文本格式的配置文件。
2、Dockerfile使用DS(Domain Specific Language)语法构建一个Docker镜像。
3、Dockerfile构建镜像更具备重复性、透明性等特性。

2 Dockerfile 的基本结构

Dockerfile 由一行行命令语句组成,并支持 # 注释。

一般而言Dockerfile分为四部分:基础镜像信息、维护着信息、镜像操作指令和容器启动时执行执行。如下:

# This Dockerfile uses the ubuntu image  
# VERSION 2 - EDITION 1  
# Author: docker_user  
# Command format: Instruction [arguments / command] ..  
# Base image to use, this must be set as the first line  
FROM centos  
# Maintainer: docker_user <docker_user at email.com> (@docker_user)  
MAINTAINER kingbase kingbase@email.com  
RUN yum update -y && yum install -y vim
# Commands when creating a new container  
CMD /usr/sbin/nginx
指令 说明
FROM unbuntu 指明所基于镜像的名称。
MAINTAINER 说明维护者信息。
RUN 每运行一条RUN指令,镜像就添加新的一层。
CMD 指定运行容器时的操作命令。

3 Dockerfile指令说明

指令一般格式为INSTRUCTION arguments,包括FROM、MAINTAINER,RUN等。参见表8-1。

指令 说明
FROM 指定所创建的基础镜像。
MAINTANER 指定维护者信息。
RUN 运行命令。
LABEL 指定生成镜像的元数据标签信息。
EXPOSE 声明镜像内服务所监听的端口。
ENV 指定环境变量。
ADD 复制指定 <src> 路径下的内容到容器中的 <dest> 路径下,<src> 可以为URL:如果为tar文件,会自动解压到 <dest> 路径下。
COPY 复制本地主机的 <src> 路径下的内容到镜像中的 <dest> 路径下;一般情况推荐使用 COPY,而不是ADD。
ENTRYPOINT 指定镜像的默认入口。
VOLUME 创建数据卷挂载点。
USER 指定运行容器是的用户名或UID。
WORKDIR 配置工作目录。
ARG 指定镜像内使用的参数(例如版本号信息等)。
ONBUILD 配置但所创建的镜像作为其他镜像的基础镜像时,所执行的创建操作指令。
STOPSIGNAL 容器退出的信号值。
SHELL 指定使用shell时的默认shell 类型。

3.1 FROM

1、概述。
指定创建镜像的基础镜像,如果本地不存在基础镜像,默认下载。
2、语法格式

FROM <image>
FROM <image>:<tag>
Warning

Dockerfile中第一条指令必须为FROM指令。并且,如果在同一个Dockerfile 中创建多个镜像,可以使用多个FROM 指令。

3.2 MAINTAINER

1、概述。
指定维护者信息。
2、语法格式。

MAINTAINER <name>

3、该信息会写入生成镜像 Author 属性域中。

3.3 RUN

1、概述。
运行指定命令。
2、语法格式。

RUN <command>
RUN ["executable","param1","param2"]
Warning

1、第一条命令默认在shell终端中运行命令,即/bin/sh -c;
2、第二条命令会使用exec 执行,不会启动shell环境。例如:
RUN ["/bin/bash","c","echo hello"]

3、每条RUN指令将在当前镜像基础上执行指定命令,并提交未新的镜像。

RUN apt-get update \  
&& apt-get install -y libsnappy-dev zlib1g-dev libbz2-dev \  
&& rm -rf /var/cache/apt

3.4 CMD

1、概述。
CMD指令用来指定启动容器的默认执行命令。
2、命令语法
(1)使用exec 执行。

CMD ["executable","param1","param2"] 

(2)使用/bin/sh 执行。

CMD command param1 param2

(3)使用ENTRYPOINT 。

CMD ["param1","param2"]
Warning

每个Dockerfile 只能有一条CMD命令。如果指定了多条命令,只有最后一条会被执行。
如果用户启动容器时手动指定了运行的命令,则会覆盖掉CMD指定的命令。

3.5 LABEL

1、概述。
LABEL 指令用来指定生成镜像的元数据标签信息。
2、语法大纲。

LABEL <key>=<value><key>=<value><key>=<value>.

3、例如:

LABEL version="1.0"  
LABEL description="This text illustrates \ that label-values can span  
multiple lines."

3.6 EXPOSE

1、概述。
声明镜像内服务所监听的端口。
2、语法大纲。

EXPOSE <port>[<port>...]

3、例如:

EXPOSE 22 80 8443 54321
Warning

注意,该指令只是起到声明作用,并不会自动完成端口映射。

3.7 ENV

1、概述。
指定环境变量,在镜像生成过程中会被后续的RUN 指令使用,在镜像启动的容器中也会存在。
2、语法大纲。

ENV <key> <values>
ENV <key>=<values>

3、示例。

ENV PG_MAJOR 14.7 
ENV PG_VERSION 14.7 
RUN curl -SL https://ftp.postgresql.org/pub/source/v14.7/postgresql-14.7.tar.gz | tar -xJC  /usr/src/postgress && ...  
ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH

3、命令说明

Warning

指定的环境变量运行时可以被覆盖掉,docker run --env <key>=<value> build_image

3.8 ADD

1、概述。
该命令将复制指定的 <src> 路径下的内容到容器中 <dest> 路径下。
2、语法格式。

ADD <src> <dest>

3、命令说明。
1、<src> 可以是
(1)Dockerfile 所在目录中的一个相对路径。
(2)可以是一个URL。
(3)还可以是一个 tar 文件。
编者注:如果是 tar 文件,会自动解压到 <dest> 路径下。
2、<dest> 可以是镜像内的绝对路径,或者相对于工作目录(WORKDIR)相对林。

3、例如:

ADD *.c /code/

3.9 COPY

1、概述。
复制本地主机的 <src> (为Dockerfile所在目录的相对路径、文件或目录)下的内容到镜像中的 <dest> 下。当目标路径不存在是,会自动创建。
2、语法格式。

COPY <src> <dest>

3.10 ENTRYPOINT

1、概述。
指定镜像的默认入口命令,该入口命令会在启动容器是作为根命令执行,所有传入值都会作为该命令的参数。

2、语法格式。
(1)使用exec 调用执行。

ENTRYPOINT ["executable","param1","param2"]

(2)使用shell中执行。

ENTRYPOINT command param1 param2

3、命令说明。
(1)当设置 ENTRYPOINT 时,CMD 指令指定的值将作为根命令的参数。

(2)每个Dockerfile 中只能有一个 ENTRYPOINT ,当指定多个是,只有最后一个有效。

(3)在运行容器时,可以被 --entrypoint 参数覆盖。

3.11 VOLUME

1、概述。
创建一个数据卷挂载点。
2、语法格式。

VOLUME ["/data"]

可以从本地主机或其他容器挂载数据卷一般用来存放数据库和需要保存的数据等。

3.12 USER

1、概述。
指定运行容器时的用户名或UID,后续的RUN 等指令也会使用指定的用户身份。
2、语法格式。

USER kingbase

3、命令说明。
当容器内的服务不需要管理员权限是,可以通过该命令指定运行用户。并可以在他之前创建所需要的用户。

RUN groupadd -r postgesql $ useradd -r -g postgres postgres

3.13 WORKDIR

1、概述。
为后续的RUN、CMD和ENTRYPOINT 指令配置工作目录。
2、语法格式。

WorkDIR /path/to/workdir

3、命令说明。
可以使用多个WORKDIR指令,后续命令如果参数是相对路径,对于编写Dockerfile 是非常方便的。

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

3.14 ARG

1、概述。
指定镜像内使用的参数(例如版本号信息等),这些参数在执行 docker build 命令时以 --build-arg <varname>=<value>
2、语法格式。

ARG <name>[=default value]

3.15 STOPSIGNAL

1、概述。
指定所创镜像启动的容器可以接收退出的信号值。
2、语法大纲。

STOPSIGNAL signal

3.16 HEALTHCHECK

1、概述。
配置启动的容器如何进行健康检查(如何判断健康与否)。
2、语法大纲。

HEALTHCHECK [OPTIONS] CMD command:

3、命令说明。

OPTION支持 说明
--interval=DURATION 默认为:30s,是指过了多久检查一次;
--timeout=DURATION 默认为:30s,每次检查等待结果超时。
--retries=N 默认为:3次,如果失败了,需要重试几次。

3.17 SHELL

1、概述。
指定其他命令使用shell时的默认shell 类型。
2、语法大纲。

SHELL ["/bin/sh","-c"]
Warning

对于Windows 系统,建议在Dockerfile开头添加
#escape=指定转义信息。

4 Dockerfile 一个简单的例子

4.1 创建Dockerfile和构建环境

$ mkdir static_web
$ cd static_web
$ touch Dockerfile

1、以上命令创建了一个名为 static_web 目录。
2、目录下存放一个 Dockerfile 文件。
3、Docker 称此环境为构建上下文。

4.2 编写一个简单的Dockerfile

# Versio:0.0.1
FROM centos:7.2.1511
MAINTAINER kingbase "kingbase@kingbase.com"
RUN yum update -y && yum install -y nginx
RUN echo 'Hi, I am in your container' > /usr/share/nginx/html/index.html
EXPOSE 80

每条指令都会创建一个新的镜像层并对镜像进行提交。Docker执行Dockerfile中的指令如下:
1、Docker从基础镜像运行一个容器。
2、执行一条命令,对容器做出修改。
3、执行类似 docker commit 的操作,提交一个新的镜像层。
4、Docker 再基于刚提交的镜像运行一个新容器。
5、执行Dockerfile中的下一条指令,直到所有指令都执行完毕。

Note

从上面的步骤可以看出,如果用户Dockerfile 由于某些原因(如某条指令失败了)没有正常结束,那么用户将得到一个可以使用镜像。
这对调试非常有帮助,可以使用最后创建的镜像进行调试。

4.3 基于Dockerfile 构建新的镜像

1、进入 static_web 目录。

$ cd static_web

2、开始构建

docker build -t="kingbase/static_web:v1"

5 Dockerfile 最佳实践

Dockerfile 指令非常灵活,需要根据自己的实际需求出发,定制适合自己的、高效方便的镜像。

本章主要介绍怎样提高编写Dockerfile 的水平。
这里也总结了一些实践经验供读者参考。

  1. 精简镜像用途:尽量让每个镜像的用途都比较集中、单一,避免构造大而复杂、多功能的镜像;
  2. 选用合适的基础镜像:过大的基础镜像会造成生成臃肿的镜像,一般推荐较为小巧的debian镜像;
  3. 提供足够清晰的命令注释和维护者信息:Dockerfile也是一种代码,需要考虑方便后续扩展和他人使用;
  4. 正确使用版本号:使用明确的版本号信息,如1.0,2.0,而非latest,将避免内容不一致可能引发的惨案;
  5. 减少镜像层数:如果希望所生成镜像的层数尽量少,则要尽量合并指令,例如多个RUN指令可以合并为一条;
  6. 及时删除临时文件和缓存文件:特别是在执行apt-get指令后,/var/cache/apt下面会缓存一些安装包
  7. 提高生成速度:如合理使用缓存,减少内容目录下的文件,或使用.dockerignore文件指定等;
  8. 调整合理的指令顺序:在开启缓存的情况下,内容不变的指令尽量放在前面,这样可以尽量复用;
  9. 减少外部源的干扰:如果确实要从外部引入数据,需要指定持久的地址,并带有版本信息,让他人可以重复而不出错。

6 Dockerfile 实战

Dockerfile 使用.dockerignore 文件
Docker Dokerfile创建SSH服务镜像
Docker Dokerfile构建KingbaseV8R6镜像